<?php
namespace Pinenut\Service\UrlRule;

use Pinenut\Service\UrlRule;

/**
 * Правило аналогичное основному правилу в yii
 *
 */
class Standart extends UrlRule{
	
	/**
	 * @var наш экшен.
	 */
	public $route;
	
	/**
	 * @var наш шаблон, из которого будет строиться рекгексп.
	 *
	 */
	public $pattern;
	
	public $defaults = [];
	 /**
     * @var строка шаблон для создания урла.
     */
    public $_template;
    /**
     * @var строка регексп для составления нового маршрута.
     */
    public $_routeRule;
    /**
     * @var массив параметров для генерации маршрута.
     */
    public $_paramRules = [];
    /**
     * @var список параметров участвующих в маршруте.
     */
    public $_routeParams = [];
    
    /**
     * Массив параметров. для которых не применяется urlencode
     * @var array
     */
    protected $_disableUrlencode = [];
    
    public $host;
	
	public function __construct($rule) {
		
		//заполняем поля правила.
		if (isset ( $rule ['pattern'] )) {
			$this->pattern = $rule ['pattern'];
		}
		
		if(isset($rule['action'])) {
			$this->route = $rule['action'];
		}

		if(isset($rule['defaults'])) {
			$this->defaults = $rule['defaults'];
		}
		
		// Массив параметров. для которых не применяется urlencode
		if(isset($rule['disableUrlencode'])) {
			$this->_disableUrlencode = (array)$rule['disableUrlencode'];
		}

		// проверяем есть ли хост, если есть паттерн пишем с учетом хоста.
		if ($this->host !== null) {
			$this->host = rtrim ( $this->host, '/' );
			$this->pattern = rtrim ( $this->host . '/' . $this->pattern, '/' );
		} elseif ($this->pattern === '') {
			$this->_template = '';
			$this->pattern = '#^$#u';
			return;
		} elseif (($pos = strpos ( $this->pattern, '://' )) !== false) {
			if (($pos2 = strpos ( $this->pattern, '/', $pos + 3 )) !== false) {
				$this->host = substr ( $this->pattern, 0, $pos2 );
			} else {
				$this->host = $this->pattern;
			}
		}
		
		//находим и заполняем все параметры в маршруте.
		if (strpos($this->route, '<') !== false && preg_match_all('/<(\w+)>/', $this->route, $matches)) {
			foreach ($matches[1] as $name) {
				$this->_routeParams[$name] = "<$name>";
			}
		}
		
		$tr = [
		'.' => '\\.',
		'*' => '\\*',
		'$' => '\\$',
		'[' => '\\[',
		']' => '\\]',
		'(' => '\\(',
		')' => '\\)',
		];
		//ищем все параметры в паттерне и заполняем массив этих параметров.
		$tr2 = [];
		if (preg_match_all ( '/<(\w+):?([^>]+)?>/', $this->pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER )) {
			foreach ( $matches as $match ) {
				
				$name = $match [1] [0];
				$pattern = isset ( $match [2] [0] ) ? $match [2] [0] : '[^\/]+';
				
				if (array_key_exists ( $name, $this->defaults )) {
					$length = strlen ( $match [0] [0] );
					$offset = $match [0] [1];
					
					if ($offset > 1 && $this->pattern [$offset - 1] === '/' && $this->pattern [$offset + $length] === '/') {
						$tr ["/<$name>"] = "(/(?P<$name>$pattern))?";
					} else {
						$tr ["<$name>"] = "(?P<$name>$pattern)?";
					}
				} else {
					$tr ["<$name>"] = "(?P<$name>$pattern)";
				}
				
				// если нашли параметр и в паттерне и в роуте, добавляем в тр2 массив.
				if (isset ( $this->_routeParams [$name] )) {
					$tr2 ["<$name>"] = "(?P<$name>$pattern)";
				} else {
					$this->_paramRules [$name] = $pattern === '[^\/]+' ? '' : "#^$pattern$#";
				}
			}
		}
		
		$this->_template = preg_replace ( '/<(\w+):?([^>]+)?>/', '<$1>', $this->pattern );
		$this->pattern = '#^' .  strtr ( $this->_template, $tr ) . '$#u';
		
		//Если есть параметры роута, то заменяем из массива тр2
		if (! empty ( $this->_routeParams )) {
			$this->_routeRule = '#^' . strtr ( $this->route, $tr2 ) . '$#u';
		}
	}
	
	public function parseUrl($url = null, $method = null, $host = null, $params = null) {
		//проверяем наш урл на регулярку.
		if (! preg_match ( $this->pattern, $url, $matches )) {
			return false;
		}
		
		foreach ( $this->defaults as $name => $value ) {
			if (! isset ( $matches [$name] ) || $matches [$name] === '') {
				$matches [$name] = $value;
			}
		}
		
		$params = array_merge($params, $this->defaults);
		
		// пытаемся вытащить из матчей параметры.
		$tr = [ ];
		
		foreach ( $matches as $name => $value ) {
			if (isset ( $this->_routeParams [$name] )) {
				$tr [$this->_routeParams [$name]] = $value;
				unset ( $params [$name] );
			} elseif (isset ( $this->_paramRules [$name] )) {
				if (in_array($name, $this->_disableUrlencode)){
					$params [$name] = $value;
				} else {
					$params [$name] = urldecode($value);
				}
				
			}
		}
		
		if ($this->_routeRule !== null) {
			$route = strtr ( $this->route, $tr );
		} else {
			$route = $this->route;
		}
// 		\Log::msg("route",$route);
// 		\Log::msg("params",$params);
		//отдаем роут и параметры в реквест.
		$request = new \Request ( $route, $params, null, false ); 
		$request->isRoot(true);
		$request->isRenderLayout(true);
		return $request;
	}
	
	public function createUrl($route, array $params, $abs = false) {
		$tr = [ ];
//		\Log::error($route,[$this->route, $params]);
		// проверяем маршрут подходит правилу.
		if ($route !== $this->route) {
			if ($this->_routeRule !== null && preg_match ( $this->_routeRule, $route, $matches )) {
				foreach ( $this->_routeParams as $name => $token ) {
					if (isset ( $this->defaults [$name] ) && strcmp ( $this->defaults [$name], $matches [$name] ) === 0) {
						$tr [$token] = '';
					} else {
						$tr [$token] = $matches [$name];
					}
				}
			} else {
				return false;
			}
		}
		
		// match default params
		// if a default param is not in the route pattern, its value must also be matched
		foreach ( $this->defaults as $name => $value ) {
			if (isset ( $this->_routeParams [$name] )) {
				continue;
			}
			if (! isset ( $params [$name] )) {
				return false;
			} elseif (strcmp ( $params [$name], $value ) === 0) { // strcmp will do string conversion automatically
				unset ( $params [$name] );
				if (isset ( $this->_paramRules [$name] )) {
					$tr ["<$name>"] = '';
				}
			} elseif (! isset ( $this->_paramRules [$name] )) {
				return false;
			}
		}
//		\Log::error('tr-'.$route, $tr);
		
		// заполняем массив параметров.
		foreach ( $this->_paramRules as $name => $rule ) {
			if (isset ( $params [$name] ) && ! is_array ( $params [$name] ) && ($rule === '' || preg_match ( $rule, $params [$name] ))) {
				if (in_array($name, $this->_disableUrlencode)){
					$tr ["<$name>"] = $params [$name];
				} else {
					$tr ["<$name>"] = urlencode ( $params [$name] );
				}
				unset ( $params [$name] );
			} elseif (! isset ( $this->defaults [$name] ) || isset ( $params [$name] )) {
				return false;
			}
		}
		
		$url = strtr ( $this->_template, $tr );
		if ($this->host !== null) {
			$pos = strpos ( $url, '/', 8 );
			if ($pos !== false) {
				$url = substr ( $url, 0, $pos ) . preg_replace ( '#/+#', '/', substr ( $url, $pos ) );
			}
		} elseif (strpos ( $url, '//' ) !== false) {
			$url = preg_replace ( '#/+#', '/', $url );
		}
		
		if (! empty ( $params ) && ($query = http_build_query ( $params )) !== '') {
			$url .= '?' . $query;
		}
		
		return $url;
	}
	
	
}